﻿#include "Snake.h"

void SetPos(short x, short y)
{
    COORD pos = { x, y };
    HANDLE hOutput = NULL;
    // 获取标准输出的句柄 ( ⽤来标识不同设备的数值 )
    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    // 设置标准输出上光标的位置为 pos
    SetConsoleCursorPosition(hOutput, pos);
}


void welcomeToGame()
{
    SetPos(35, 13);
    printf(" 欢迎来到贪吃蛇小游戏！ ");
    SetPos(37, 27); // 让按任意键继续的出现的位置好看点
    system("pause");
    system("cls"); //清屏幕
    SetPos(20, 13);
    printf(" 用 ↑ . ↓ . ← . → 分别控制蛇的移动，F3 为加速， F4 为减速 \n");
    SetPos(35, 14);
    printf(" 加速将能得到更高的分数。 \n");
    SetPos(37, 27); // 让按任意键继续的出现的位置好看点
    system("pause");
    system("cls");                                                      
    
}


void CreateMap()
{
    int i = 0;
    // 上 
    SetPos(0, 0);
    for (i = 0; i < 58; i += 2)
    {
        wprintf(L"%lc", WALL);
    }
    // 下 
    SetPos(0, 26);
    for (i = 0; i < 58; i += 2)
    {
        wprintf(L"%lc", WALL);
    }
    // 左
    //x 是 0 ， y 从 1 开始增⻓
    for (i = 1; i < 26; i++)
    {
        SetPos(0, i);
        wprintf(L"%lc", WALL);
    }
    // 右  x 是 56 ， y 从 1 开始增⻓
    for (i = 1; i < 26; i++)
    {
        SetPos(56, i);
        wprintf(L"%lc", WALL);
    }
}


void InitSnake(psnake ps)
{
    int i = 0;
    pSnakeNode cur = NULL;
    for (i = 0; i < 5; i++)
    {
        cur = (pSnakeNode)malloc(sizeof(SnakeNode));
        if (cur == NULL)
        {
            perror("InitSnake");
            return;
        }
        cur->next = NULL;
        cur->x = POS_X + 2 * i;
        cur->y = POS_Y;
        // 头插法
        if (ps->_pSnake == NULL)
        {
            ps->_pSnake = cur;
        }
        else
        {
            cur->next = ps->_pSnake;
            ps->_pSnake = cur;
        }
    }
    cur = ps->_pSnake;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", BODY);
        cur = cur->next;
    }
    // 初始化贪吃蛇数据
    ps->_sleep_time = 200;
    ps->_scor = 0;
    ps->_sta = OK;
    ps->_dir = RIGHT;
    ps->_food_weight = 10;
}


void CreateFood(psnake ps)
{
    int x = 0;
    int y = 0;
again:
    // 产⽣的 x 坐标应该是 2 的倍数，这样才可能和蛇头坐标对⻬。
    do
    {
        x = rand() % 53 + 2;
        y = rand() % 25 + 1;
    } while (x % 2 != 0);
    pSnakeNode cur = ps->_pSnake; // 获取指向蛇头的指针
    while (cur) // ⻝物不能和蛇⾝冲突
    {
        if (cur->x == x && cur->y == y)
        {
            goto again;
        }
        cur = cur->next;
    }
    pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); // 创建⻝物
    if (pFood == NULL)
    {
        perror("CreateFood::malloc()");
        return;
    }
    else
    {
        pFood->x = x;
        pFood->y = y;
        SetPos(pFood->x, pFood->y);
        wprintf(L"%lc", FOOD);
        ps->_pfood = pFood;
    }
}


void GameStart(psnake ps)
{
 
    system("mode con cols=100 lines=30"); //设置窗口的大小
    system("title 贪吃蛇"); //设置窗口名字
    HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); // 影藏光标操作
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(hOutput, &CursorInfo); // 获取控制台光标信息
    CursorInfo.bVisible = false; // 隐藏控制台光标
    SetConsoleCursorInfo(hOutput, &CursorInfo); // 设置控制台光标状态

    //1.打印环境页面和功能介绍
    welcomeToGame();
    //2.绘制地图
    CreateMap();
    //3.创建蛇
    InitSnake(ps);
    //4.创建食物
    CreateFood(ps);
}


void PrintHelpInfo()
{
    // 打印提⽰信息
    SetPos(61, 11);
    printf(" 不能穿墙，不能咬到自己 \n");
    SetPos(61, 12);
    printf(" 用 ↑  ↓  ←  → 分别控制蛇的移动 ");
    SetPos(62, 13);
    printf("F3 为加速， F4 为减速 \n");
    SetPos(62, 14);
    printf("ESC ：退出游戏  space ：暂停游戏 .");
}
    
  
void pause()
{
    while (1)
    {
        Sleep(200);
        if (KEY_PRESS(VK_SPACE))
        {
            break;
        }
    }
}


int NextIsFood(pSnakeNode pS, psnake ps)
{
    return (pS->x == ps->_pfood->x) && (pS->y == ps->_pfood->y);
}


void EatFood(pSnakeNode psn, psnake ps)
{
    // 头插法
    psn->next = ps->_pSnake;
    ps->_pSnake = psn;
    // 打印蛇
    pSnakeNode cur = ps->_pSnake;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%c", BODY);
        cur = cur->next;
    }
    ps->_scor += ps->_food_weight;
    // 释放⻝物节点
    free(ps->_pfood);
    // 创建新的⻝物
    CreateFood(ps);
}


void NoFood(pSnakeNode psn, psnake ps)
{
    // 头插法
    psn->next = ps->_pSnake;
    ps->_pSnake = psn;
    // 打印蛇
    pSnakeNode cur = ps->_pSnake;
    while(cur->next->next != NULL)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%c", BODY);
        cur = cur->next;
    }
    // 最后⼀个位置打印空格，然后释放节点
    SetPos(cur->next->x, cur->next->y);
    printf("  ");
    free(cur->next);
    cur->next = NULL;
}


void KillByWall(psnake ps)
{
    if ((ps->_pSnake->x == 0)
        || (ps->_pSnake->x == 56)
        || (ps->_pSnake->y == 0)
        || (ps->_pSnake->y == 26))
    {
        ps->_sta = KILL_BY_WALL;
    }
    return 0;
}


void KillBySelf(psnake ps)
{
    pSnakeNode cur = ps->_pSnake->next;
    while (cur)
    {
        if ((ps->_pSnake->x == cur->x)
            && (ps->_pSnake->y == cur->y))
        {
            ps->_sta = KILL_BY_SELF;
            break;
        }
        cur = cur->next;
    }
}
 

void SnakeMove(psnake ps)
{
    pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
    if (pNextNode == NULL)
    {
        perror("SnakeMove()::malloc()");
        return;
    }
    switch (ps->_dir)
    {
    case UP:
    {
        pNextNode->x = ps->_pSnake->x;
        pNextNode->y = ps->_pSnake->y - 1;
    }
    break;
    case DOWN:
    {
        pNextNode->x = ps->_pSnake->x;
        pNextNode->y = ps->_pSnake->y + 1;
    }
    break;
    case LEFT:
    {
        pNextNode->x = ps->_pSnake->x - 2;
        pNextNode->y = ps->_pSnake->y;
    }
    break;
    case RIGHT:
    {
        pNextNode->x = ps->_pSnake->x + 2;
        pNextNode->y = ps->_pSnake->y;
    }
    break;
    }
    // 如果下⼀个位置就是⻝物
    if (NextIsFood(pNextNode, ps))
    {
        EatFood(pNextNode, ps);
    }
    else // 如果没有⻝物
    {
        NoFood(pNextNode, ps);
    }
    KillByWall(ps);
    KillBySelf(ps);
}


void GameRun(psnake ps)
{
    // 打印右侧帮助信息
    PrintHelpInfo();
    do
    {
        SetPos(61, 20);
        printf(" 得分： %d  ", ps->_scor);
        printf(" 每个食物得分： %2d 分 ", ps->_food_weight);
        if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
        {
            ps->_dir = UP;
        }
        else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
        {
            ps->_dir = DOWN;
        }
        else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
        {
            ps->_dir = LEFT;
        }
        else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
        {
            ps->_dir = RIGHT;
        }
        else if (KEY_PRESS(VK_SPACE))
        {
            //暂停
            pause();
        }
        else if (KEY_PRESS(VK_ESCAPE))//正常退出游戏
        {
            
            ps->_sta = END_NORMAL;
            break;
        }
        else if (KEY_PRESS(VK_F3)) //加速
        {
            if (ps->_sleep_time >= 80)
            {
                ps->_sleep_time -= 30;
                ps->_food_weight += 2; // ⼀个⻝物分数最⾼是 20 分
            }
        }
        else if (KEY_PRESS(VK_F4)) //减速
        {
            if (ps->_sleep_time < 320)
            {
                ps->_sleep_time += 30;
                ps->_food_weight -= 2; // ⼀个⻝物分数最低是 2 分
            }
        }
        // 蛇每次⼀定之间要休眠的时间，时间短，蛇移动速度就快
        SnakeMove(ps);
        Sleep(ps->_sleep_time);
    } while (ps->_sta == OK);
}


void GameEnd(psnake ps)
{
    pSnakeNode cur = ps->_pSnake;
    SetPos(24, 12);
    switch (ps->_sta)
    {
    case END_NORMAL:
        printf(" 您主动退出游戏 \n");
        break;
    case KILL_BY_SELF:
        printf(" 您撞上⾃⼰了 , 游戏结束 !\n");
        break;
    case KILL_BY_WALL:
        printf(" 您撞墙了 , 游戏结束 !\n");
        break;
    }
    // 释放蛇⾝的节点
    while (cur)
    {
        pSnakeNode del = cur;
        cur = cur->next;
        free(del);
    }
}